package com.yumeng.common.api;

import com.yumeng.common.exception.YumengException;
import com.yumeng.common.http.HttpResult;
import com.yumeng.common.runtime.ThreadLocalHelper;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import java.io.Serializable;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wxd
 * @date 2021/5/25 14:07
 */
@Getter
@Builder
@AllArgsConstructor
@Accessors(chain = true)
@Schema(name = "ApiResult", description = "响应结果集")
public class ApiResult implements Serializable {

    private static final long serialVersionUID = 1L;

    @Schema(description = "响应码")
    private Integer code;
    @Schema(description = "消息")
    private String msg;
    @Schema(description = "数据")
    private Object data;
    @Schema(description = "扩展信息")
    private Map<String, Object> extInfo;
    @Schema(description = "响应时间")
    private final Date time;

    public ApiResult(){
        this.time = new Date();
    }

    //region success

    public static ApiResult success(){
        return result(ApiCode.SUCCESS,null, null);
    }
    public static ApiResult successData(Object data){
        checkData(data);
        return result(ApiCode.SUCCESS, data,null);
    }
    public static ApiResult successPage(Object page){
        return result(ApiCode.SUCCESS, page,null);
    }
    public static ApiResult successMsg(String message){
        return result(ApiCode.SUCCESS, null, message);
    }
    public static ApiResult successDataMsg(Object data,String message){
        checkData(data);
        return result(ApiCode.SUCCESS, data, message);
    }

    //endregion

    //region fail

    public static ApiResult fail(){
        return result(ApiCode.FAIL, null, null);
    }
    public static ApiResult failMsg(String message){
        return result(ApiCode.FAIL, null, message);
    }
    public static ApiResult failData(Object data){
        return result(ApiCode.FAIL, data,null);
    }
    public static ApiResult failDataMsg(Object data,String message){
        return result(ApiCode.FAIL, data, message);
    }
    public static ApiResult resultFlag(boolean flag){
        if (flag){
            return success();
        }
        return fail();
    }

    //endregion

    //region error

    public static ApiResult error(){
        return result(ApiCode.ERROR, null, null);
    }
    public static ApiResult errorMsg(String message){
        return result(ApiCode.ERROR, null, message);
    }
    public static ApiResult resultErrorFlag(boolean flag){
        if (flag){
            return success();
        }
        return error();
    }

    //endregion

    //region businessError

    public static ApiResult businessError(){
        return result(ApiCode.BUSINESS_EXCEPTION, null, null);
    }
    public static ApiResult businessError(String message){
        return result(ApiCode.BUSINESS_EXCEPTION, null, message);
    }
/*    public static ApiResult<Object> resultBusinessFlag(boolean flag){
        if (flag){
            return success();
        }
        return businessError();
    }*/

    //endregion

    //region yumengError

    public static ApiResult yumengError(){
        return result(ApiCode.YUMENG_EXCEPTION, null, null);
    }
    public static ApiResult yumengError(String message){
        return result(ApiCode.YUMENG_EXCEPTION, null, message);
    }
/*    public static ApiResult<Object> resultYumengFlag(boolean flag){
        if (flag){
            return success();
        }
        return yumengError();
    }*/

    //endregion

    //region apiCode

    public static ApiResult resultDataMsg(ApiCode apiCode, Object data, String message){
        return result(apiCode, data, message);
    }

    public static ApiResult resultMsg(ApiCode apiCode, String message){
        return result(apiCode, null, message);
    }

    public static  ApiResult resultData(ApiCode apiCode, Object data){
        return result(apiCode, data,null);
    }

    public static ApiResult resultHttp(HttpResult result){
        if (result.isSuccess()){
            return successData(result.getData());
        }else {
            return failMsg(result.getMsg());
        }
    }

    //endregion

    //region 二进制流

    public static ResponseEntity<byte[]> jpegStream(byte[] binaryData){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_JPEG);
        return new ResponseEntity<>(binaryData, headers, HttpStatus.OK);
    }

    public static ResponseEntity<byte[]> pngStream(byte[] binaryData){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);
        return new ResponseEntity<>(binaryData, headers, HttpStatus.OK);
    }

    public static ResponseEntity<byte[]> gifStream(byte[] binaryData){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_GIF);
        return new ResponseEntity<>(binaryData, headers, HttpStatus.OK);
    }

    public static ResponseEntity<byte[]> binaryStream(byte[] binaryData){
        return binaryStream(binaryData, null);
    }

    public static ResponseEntity<byte[]> binaryStream(byte[] binaryData, String fileName){
        fileName = StringUtils.isNotBlank(fileName) ? fileName : "未命名文件-" + System.currentTimeMillis();
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentDispositionFormData("attachment", fileName);
        return new ResponseEntity<>(binaryData, headers, HttpStatus.OK);
    }

    //endregion


    private static ApiResult result(ApiCode apiCode, Object data, String message){
        ApiCode tempCode = apiCode;
        ApiCode localApiCode = ThreadLocalHelper.getApiCode();
        if (localApiCode != null){
            tempCode = localApiCode;
        }
        if (tempCode == null){
            tempCode = ApiCode.UNKNOWN_ERROR;
        }
        String msg = StringUtils.isNotBlank(message) ? message : tempCode.getMessage();
        return ApiResult.builder()
                .code(tempCode.getCode())
                .msg(msg)
                .data(data)
                .extInfo(getResultExtInfo(tempCode))
                .time(new Date())
                .build();
    }

    /**
     * 一些扩展信息，指导前端执行返回登录、刷新token、修改密码等操作
     *
     * @param apiCode
     * @return
     */
    private static Map<String, Object> getResultExtInfo(ApiCode apiCode){
        boolean needReLogin = ApiCode.needReLogin(apiCode);//是否需要重新登录
        boolean needRefreshToken = ApiCode.needRefreshToken(apiCode);//是否需要刷新token
        boolean needChangeCredentials = ApiCode.needChangeCredentials(apiCode);//是否需要修改密码
        Map<String, Object> extInfo = new HashMap<>();
        if (needReLogin){
            extInfo.put("reLogin", true);
        }
        if (needRefreshToken){
            extInfo.put("refreshToken", true);
        }
        if (needChangeCredentials){
            extInfo.put("changeCredentials", true);
        }
        return extInfo;
    }

    private static void checkData(Object data){
        if (data == null){
            throw new YumengException("数据为空");
        }
    }

}
